"""
A class to store information 'About' a column of data. 

The key method is 'keep' which stores information about a new datum in
a set of contexts. For example, if a row has a class 'car' then
details about the cells in that car will be stored in in 'car'.

Often, one of contexts is the special context 'About.overall'. For
numeric class values, it is good practice to only use 'About.overall'
(otherwise the data will be split into silly bins).

The real work of this code is done by Num or Sym objects.  In fact,
most of 'About' is a traffic code that keeps details on Nums or Syms
stored in different contexts.

This code is part of AXE:           /'-./\_
cropping complexity,                :   ||,>
culling confusion,                  \.-'||
and cutting the crap                    ||
since 2012.                             ||  
                                        ||   pjb

(c) 2012, Tim Menzies, tim@menzies.us, LGPL v3.0

Bugs
----
If a class name is the same as 'About.overall', this code will get
confused.

"""

from thing import *

class About(object) :
  overall = "__overall__"
  def __init__(self,n,name,weight,
               details=Sym,klassp=False): 
    """
    'contexts' is a place to store all contexts.
    'cllover' is some place to store summaries from all contexts.
    'aetails' is class from which we generate object to store data.
    'index' is a back index remembering what rows have some data 
    (this index will be used to optimize rule-based programming, one day).
    """
    self.weight  = weight
    self.details = details
    self.klassp  = klassp
    self.name    = name
    self.pos     = n
    self.contexts = Dict()
    self.contexts[About.overall] = details()
    self.index=Dict()

  def clone(self) :
    "Returns the same kind on 'About' object with counts zero-ed out."
    return About(self.pos,self.name,self.weight,
                 self.details,self.klassp)

  def isNumeric(self) :
    "Returns true if this column is about numerics."
    return self.details == Num

  def all(self):
    "Returns the data held in the 'overall' context."
    return self.contexts[About.overall]

  def keep(self,x,row,ats) :
    """
    Ignore unknown values, remember what row had this datum,
    then store information about 'x' in the 'allover' context
    as well at the context named 'at'.
    """
    if x== '?': return x
    self.index.push(x,row)
    for at in ats:
      out = self.store(at,x,row) 
    return out

  def store(self,at,x,row) :
    """
    Keep something in a context. If we have no keeper yet,
    then create one from 'details'.
    """
    context = self.contexts.at(at,(lambda : self.details()))
    return context.keep(x)
